/*****************************************************************************
 * dev_weight.c
 *
 * Copyright (C) 2020 Jeasonvor 1101627719@qq.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"
#include "dev_weight.h"

/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL   LOG_LEVEL_DEBUG
#define LOG_MODULE      "dev.sensor"
#include "thirdparty/log/log.h" 
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
#define    DEV_CMD_ID_BOARDCAST 0xFF                //广播地址


//protocol
#define     DEV_CMD_TIEMOUT      1000               //ms
#define     DEV_CMD_RETRTY       3
#define     DEV_CMD_HEADER       0x02FD             //协议头
//commander
#define     DEV_CMD_R                   0x72        //读取 Raw Count 值
#define     DEV_CMD_W                   0x77        //读取标准重量
#define     DEV_CMD_H                   0x68        //读取全部重量
#define     DEV_CMD_X                   0x78        //读取其它类型传感器数据
#define     DEV_CMD_S                   0x73        //设置地址
#define     DEV_CMD_P                   0x70        //读取参数信息
#define     DEV_CMD_Q                   0x71        //写入参数信息
#define     DEV_CMD_M                   0x6D        //设置工作模式
#define     DEV_CMD_N                   0x4E        //写入广播标志信息
#define     DEV_CMD_T                   0x74        //通过匹配指定参数设置地址
#define     DEV_CMD_C                   0x63        //校准
#define     DEV_CMD_Z                   0x7A        //置零
#define     DEV_CMD_K                   0x53        //按键状态触发上报
#define     DEV_CMD_U                   0x7A        //固件升级
#define     DEV_CMD_L                   0x6C        //电子标签控制
#define     DEV_CMD_F                   0x66        //执行特定功能

//error code
#define     DEV_CMD_ECODE_OK            0x00        //成功
#define     DEV_CMD_ECODE_IDX           0x01        //非法索引
#define     DEV_CMD_ECODE_DATA          0x02        //数据错误
#define     DEV_CMD_ECODE_SENSOR        0x03        //传感器故障
#define     DEV_CMD_ECODE_MEM           0x04        //存储故障
#define     DEV_CMD_ECODE_STA           0x05        //状态错误
#define     DEV_CMD_ECODE_COM           0x06        //通讯错误
#define     DEV_CMD_ECODE_UNK           0xFF        //未知错误
/*****************************************************************************
 * Private Types
 ****************************************************************************/
typedef struct{
    plum_u8         *data;
    plum_u8         len;
}payload_t;

typedef struct{
    plum_u8     ver;
    plum_u8     id;
    plum_u8     cmd;
    payload_t   payload;
}weight_param_t;

typedef struct{
    plum_u8     version;
    plum_s32    (*io_read)(plum_u8 *data,plum_u32 size,plum_u32 timeout);
    plum_s32    (*io_write)(plum_u8 *data,plum_u32 size);
}weight_t;

typedef struct{
    plum_u32    raw_scount;     //静态count
    plum_u32    raw_dcount;     //动态count
    plum_float  std_weight;     //标准重量


}dev_reg_t;

typedef plum_s32    (*protocol_parase_t)(plum_u8 *data,plum_u32 len,plum_void *out);

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/
PLUM_PRIVATE
weight_t    weight_hdl;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
plum_s8     *dev_weight_err(plum_u8 code)
{
    switch(code){
        case DEV_CMD_ECODE_OK:
            return "成功";
        case DEV_CMD_ECODE_IDX:
            return "非法索引值";
        case DEV_CMD_ECODE_DATA:
            return "数据错误";
        case DEV_CMD_ECODE_SENSOR:
            return "传感器故障";
        case DEV_CMD_ECODE_MEM:
            return "存储错误";
        case DEV_CMD_ECODE_STA:
            return "状态错误";
        case DEV_CMD_ECODE_COM:
            return "通讯故障";
        default:
            return "未知错误";
    }
}
//异或
PLUM_PRIVATE
plum_u8     dev_weight_xor(plum_u8 *data,plum_u32 len)
{   
    plum_u8     xor = data[0];

    plum_u32 loop;
    for(loop=1;loop<len;loop++){
        xor ^= data[loop];
    }

    return (xor);
}

/**
 * @brief:  按照协议发送数据
 * 
 * @param[in]:  param       -   待发数据参数指针
 * @param[in]:  cb          -   参会参数处理回调
 * @param[in]:  out         -   回调参数
 * **/
PLUM_PRIVATE
plum_s32    dev_weight_write_command(weight_param_t *param,protocol_parase_t cb,void *out)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!param){
            LOG_E("the write paramter is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        plum_u8   cmd[32] = {DEV_CMD_HEADER >> 8,DEV_CMD_HEADER & 0xFF,};
        cmd[2] = 4 + param->payload.len;
        cmd[3] = weight_hdl.version;
        cmd[4] = param->id;
        cmd[5] = param->cmd;
        memcpy(cmd+6,param->payload.data,param->payload.len);
        cmd[6+param->payload.len] = dev_weight_xor(cmd+3,4+param->payload.len);

        plum_u8     loop;
        for(loop=0;loop<DEV_CMD_RETRTY;loop++){
            LOG_DHEX("write cmd:",cmd,7+param->payload.len);
            rc = weight_hdl.io_write(cmd,7+param->payload.len);
            if(rc < 0){
                LOG_E("weight write cmd err,rc: %d",rc);
                break;
            }

            plum_u8   recv[512] = {0};
            rc = weight_hdl.io_read(recv,sizeof(recv),DEV_CMD_TIEMOUT);
            if(rc <= 0){
                LOG_E("weight recv cmd err,rc: %d",rc);
                break;
            }
            LOG_DHEX("recv cmd :",recv,rc);
            
            if(dev_weight_xor(recv+3,rc-4) != recv[rc-1]){
                LOG_E("the recv msg crc is err");
                rc = PLUM_ECODE_EPARA;
                break;
            }

            if(cb){
                rc = cb(recv,rc,out);
                if(rc){
                    LOG_E("call param function err,rc :%d",rc);
                    continue;
                }
                break;
            }
        }
    }while(0);

    return (rc);
}

PLUM_PRIVATE
plum_s32  dev_weight_raw_count(plum_u8 *payload,plum_u32 len,mit_raw_cnt_t *raw)
{
    plum_s32        rc = PLUM_ECODE_OK;

    do{
        if(!payload){
            LOG_E("the payload pinter is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if(payload[5] != DEV_CMD_R - 0x20){
            LOG_E("the payload commander err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        raw->dynamic_cnt    =  (payload[6] << 24) | (payload[7] << 16) | (payload[8] << 8) | payload[9];
        raw->weight_cnt     =  (payload[10] << 24) | (payload[11] << 16) | (payload[12] << 8) | payload[13];
    }while(0);

    return (rc);
}  

PLUM_PRIVATE
plum_s32    dev_weight_std_weight(plum_u8 *payload,plum_u32 len,mit_std_wht_t *std)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!payload){
            LOG_E("the payload pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(payload[5] != (DEV_CMD_W - 0x20)){
            LOG_E("the payload commander is err");
            rc = PLUM_ECODE_EPARA;
            break;
        }
    
        if(std){
            plum_s8 str[9] = {0};
            memcpy(str,payload+7,8);
            std->std_wweight = atof(str);
            std->flag = payload[6];
        }
    }while(0);

    return (rc);
}

PLUM_PRIVATE
plum_s32    dev_weight_all_weight(plum_u8 *payload,plum_u32 len,mit_total_wht_t *total)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!payload){
            LOG_E("the payload pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(payload[5] != (DEV_CMD_H - 0x20)){
            LOG_E("the payload commander is err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if(total){
            total->increase = payload[6];
            total->flag = payload[7];
            snprintf(total->str,sizeof(total->str),"%s",payload+8);
            total->value = *(plum_float *)(payload + 16);
            total->fix  = *(plum_float *)(payload + 20);
        }

    }while(0);

    return (rc); 
}

PLUM_PRIVATE
plum_s32    dev_weight_all_param(plum_u8 *payload,plum_u32 len,mit_all_param_t *all)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!payload){
            LOG_E("the payload pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(payload[5] != (DEV_CMD_P - 0x20)){
            LOG_E("the payload commander is err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        if(all){

        }
    }while(0);

    return (rc);
}

PLUM_PRIVATE
plum_s32    dev_weight_rasp_handle(plum_u8 *payload,plum_u32 len,mit_all_param_t *all)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!payload){
            LOG_E("the payload pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        LOG_E("%s",dev_weight_err(payload[6]));

        switch(payload[6]){
            case DEV_CMD_ECODE_OK:
                rc = PLUM_ECODE_OK; 
                break;

            case DEV_CMD_ECODE_DATA:
            case DEV_CMD_ECODE_IDX:
                rc = PLUM_ECODE_EPARA;
                break;

            case DEV_CMD_ECODE_MEM:
                rc = PLUM_ECODE_EMEM;
                break;

            default :
                rc = PLUM_ECODE_UNK;
                break;
        }
    }while(0);

    return (rc);
}
/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32    dev_weight_protocol_register(protocol_version_t version,    
                    plum_s32 (*io_write)(plum_u8 *data,plum_u32 size),
                    plum_s32 (*io_read)(plum_u8 *data,plum_u32 size,plum_u32 timeout))
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(!io_write || !io_read){
            rc = PLUM_ECODE_INVALID;
            LOG_E("the io interface is null");
            break;
        }

        weight_hdl.version = version;
        weight_hdl.io_read = io_read;
        weight_hdl.io_write = io_write;
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_raw_count(plum_u8 id,mit_raw_cnt_t *value)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{ 
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(!value){
            LOG_E("the value pointer is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        weight_param_t  param = {
            .cmd = DEV_CMD_R,
            .id = id,
            .payload.data = plum_null,
            .payload.len = 0,
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_raw_count,value); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_std_weight(plum_u8 id,mit_std_wht_t *value)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(!value){
            LOG_E("the value pointer is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        weight_param_t  param = {
            .cmd = DEV_CMD_W,
            .id = id,
            .payload.data = plum_null,
            .payload.len = 0,
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_std_weight,value); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_all_weight(plum_u8 id,mit_total_wht_t *value)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(!value){
            LOG_E("the value pointer is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        weight_param_t  param = {
            .cmd = DEV_CMD_H,
            .id = id,
            .payload.data = plum_null,
            .payload.len = 0,
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_all_weight,value); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_all_reg(plum_u8 id,mit_all_reg_t *value)
{
   plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if(!value){
            LOG_E("the value pointer is null");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        plum_u8 cmd_tbl[] = {0x01,0x02,0x03,0x04,0x05,0x06,
                            0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
                            0x30,0x32,0x35,0x36,0x37,
                            0x40,
                            0x50,0x54,
                            0x60,0x62,
                            0x70,0x71};
        weight_param_t  param = {
            .cmd = DEV_CMD_P,
            .id = id,
            .payload.data = cmd_tbl,
            .payload.len = sizeof(cmd_tbl),
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_all_param,value); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);  
}

PLUM_PUBLIC
plum_s32    dev_mit_clean_id(plum_u8 id)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }
        
        plum_u8    value = id;
        weight_param_t  param = {
            .cmd = DEV_CMD_S,
            .id = id,
            .payload.data = &value,
            .payload.len = 1,
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,plum_null,plum_null); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }

        
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_mode_set(plum_u8 id,mit_mode_t mode)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        plum_u8 value[] = {mode};
        weight_param_t  param = {
            .cmd = DEV_CMD_M,
            .id = id,
            .payload.data = value,
            .payload.len = sizeof(value),
            .ver = weight_hdl.version,
        };

        rc = dev_weight_write_command(&param,dev_weight_rasp_handle,plum_null); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}

PLUM_PUBLIC
plum_s32    dev_mit_calibration(plum_u8 id)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        plum_u8 value[] = {0,0,0,0,0};
        weight_param_t  param = {
            .cmd = DEV_CMD_C,
            .id = id,
            .payload.data = value,
            .payload.len = sizeof(value),
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_rasp_handle,plum_null); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}

plum_s32    dev_mit_power_ctrl(plum_u8 id,mit_power_t power)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        plum_u8 off[] = {0,0,0,1,0,0,1,0xF4};
        plum_u8 reboot[] = {0,0,0,2,0,0,1,0xF4};
        plum_u8 *value = (power == MIT_POWER_OFF) ? off : reboot;
        weight_param_t  param = {
            .cmd = DEV_CMD_F,
            .id = id,
            .payload.data = value,
            .payload.len = sizeof(off),
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_rasp_handle,plum_null); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}


plum_s32    dev_mit_label_display(plum_u8 id,plum_s8 *name,plum_u32 cnt)
{
    plum_s32    rc = PLUM_ECODE_OK;

    do{
        if(id == DEV_CMD_ID_BOARDCAST){
            LOG_E("not support the id : %d",id);
            rc = PLUM_ECODE_INVALID;
            break;
        }

        plum_u8 value[64] = {0xA2,0x00,0x01,};
        memcpy(value,name,cnt);
        weight_param_t  param = {
            .cmd = DEV_CMD_L,
            .id = id,
            .payload.data = value,
            .payload.len = cnt + 3,
            .ver = weight_hdl.version,
        };
        rc = dev_weight_write_command(&param,dev_weight_rasp_handle,plum_null); 
        if(rc){
            LOG_E("dev_weight_write_command err,rc : %d",rc);
            break;
        }
    }while(0);

    return (rc);
}
/*****************************************************************************
 * Cli Functions
 ****************************************************************************/

plum_void  cli_weigth_raw_read(plum_u32 argc, plum_s8 *argv[])
{
    if(argc != 1){
        LOG_E("the arg is err,format 'raw_read [id]' ");
        return;
    }
    plum_u32 id = strtoul(argv[0],NULL,10);
    LOG_I("id : %d",id);

    mit_raw_cnt_t value = {0};
    dev_mit_raw_count(id,&value);
    LOG_I("raw dynamic cnt  : 0x%08X",value.dynamic_cnt);
    LOG_I("raw weight cnt   : 0x%08X",value.weight_cnt);
}
PLUM_CAL_CLI_EXPORT("raw_read","read weight raw count",cli_weigth_raw_read);

plum_void  cli_weigth_std_read(plum_u32 argc, plum_s8 *argv[])
{
    if(argc != 1){
        LOG_E("the arg is err,format 'std_read [id]' ");
        return;
    }

    plum_u32 id = strtoul(argv[0],NULL,10);
    LOG_I("id : %d",id);

    mit_std_wht_t value = {0};
    dev_mit_std_weight(id,&value);
    LOG_I("std flag    : %c",value.flag);
    LOG_I("std weight  : %f",value.std_wweight);
}
PLUM_CAL_CLI_EXPORT("std_read","read weight std count",cli_weigth_std_read);

plum_void   cli_weight_all_weight_read(plum_u32 argc,plum_s8 *argv[])
{
    if(argc != 1){
        LOG_E("the arg is err,format 'total_read [id]' ");
        return;
    } 
       
    plum_u32 id = strtoul(argv[0],NULL,10);
    LOG_I("id : %d",id);

    mit_total_wht_t value = {0};
    dev_mit_all_weight(id,&value);
    LOG_I("total increase: %d",value.increase);
    LOG_I("total flag    : %c",value.flag);
    LOG_I("total str     : %s",value.str);
    LOG_I("total value   : %f",value.value);
    LOG_I("total fix     : %f",value.fix);
}
PLUM_CAL_CLI_EXPORT("total_read","read weight total ",cli_weight_all_weight_read);

plum_void   cli_weight_all_reg_read(plum_u32 argc,plum_s8 *argv[])
{
    if(argc != 1){
        LOG_E("the arg is err,format 'reg_read [id]' ");
        return;
    } 
       
    plum_u32 id = strtoul(argv[0],NULL,10);
    LOG_I("id : %d",id);

    mit_all_reg_t   reg = {0};
    dev_mit_all_reg(id,&reg);
    
}
PLUM_CAL_CLI_EXPORT("reg_read","read weight reg ",cli_weight_all_reg_read);

plum_void   cli_weight_power_ctrl(plum_u32 argc,plum_s8 *argv[])
{
    if(argc != 2){
        LOG_E("the arg is err,format 'pw_ctrl [id] [sta]' ");
        return;
    } 
       
    plum_u32 id = strtoul(argv[0],NULL,10);
    plum_u32 sta = strtoul(argv[1],NULL,10);
    LOG_I("id : %d",id);
    LOG_I("power : %d",sta);

    dev_mit_power_ctrl(id,sta);
}
PLUM_CAL_CLI_EXPORT("pw_ctrl","ctrol weight power off(1)/reset(2) ",cli_weight_power_ctrl);

plum_void   cli_weight_calibration(plum_u32 argc,plum_s8 *argv[])
{
    if(argc != 1){
        LOG_E("the arg is err,format 'calib [id]' ");
        return;
    } 
       
    plum_u32 id = strtoul(argv[0],NULL,10);
    LOG_I("id : %d",id);

    dev_mit_calibration(id);
}
PLUM_CAL_CLI_EXPORT("calib","calibration the weigh",cli_weight_calibration);

plum_void   cli_weight_display(plum_u32 argc,plum_u8 *argv[])
{
    if(argc != 3){
        LOG_E("the arg is err,format 'display [id] [name] [cnt]' ");
        return;
    } 
       
    plum_u32 id = strtoul(argv[0],NULL,10);
    plum_s8 *name = argv[1];
    plum_u32 cnt = strtoul(argv[2],NULL,10);

    LOG_I("id : %d",id);  
    LOG_I("name : %s",name);
    LOG_I("cnt : %d",cnt);

    dev_mit_label_display(id,name,cnt);
}
PLUM_CAL_CLI_EXPORT("display","display the weigh",cli_weight_display);

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
